Restrict and register image sizes
What you'll see
You just changed sizePolicy, or you are adopting WebP/AVIF, and something breaks. The symptoms:
- Images suddenly return 400 after switching to
strict— a size the page requests is not inallowedSizes. - An image comes back at a “nearly right” size under
auto— it was snapped to the closest allowed size. - A size generated in client-side JS never shows up in “Scan now” — the scan only reads server-side source.
.webp/.avifis served as JPEG for as long as the policy isopen.- Scaled images return 429 under load — the anti-DoS rate guard on scaling.
What's actually happening
The size key is WxHxM — the same string as the URL segment. WxH (no third part) is mode 0. There are three policy modes:
| Mode | Behaviour |
|---|---|
open (default) | Any size, only the maxDimension cap applies. .webp/.avif on the URL path is downgraded to JPEG/PNG. |
auto | Accepts allowedSizes ∪ discoveredSizes. An unknown size snaps to the nearest allowed one; the list self-expands from traffic and scan. |
strict | Same as auto, but an unknown size returns HTTP 400. No self-expansion. |
Two lists with different ownership: allowedSizes is yours (owner-maintained, never touched by scan or passive discovery), while discoveredSizes is the machine's (append-only, deduped, passive growth capped at 100).
Snapping under auto only matches within the same scaling mode. If no allowed size exists with the requested mode, the request is rejected instead of snapped — that is the “nearly right size” symptom when the mode does not line up.
Under open, a requested .webp/.avif on the URL path is downgraded to JPEG/PNG. This is a DoS defence (see Use AVIF and WebP image formats, #26952): open has no size whitelist, so the URL could otherwise request arbitrary sizes in the heavier formats. Server-authored docly.scaleImage and attachFile64 are not subject to the policy.
What to do
Roll the policy on in order — start permissive, capture the real sizes, then lock down.
- Open Folder Properties → the “Image sizes” tab (requires publish rights on the folder).
- Set
sizePolicytoauto. This starts passive discovery and lets you scan. - Run “Scan now” (a static dry-run — it writes nothing), review the diff, then Apply changes.
- Promote the sizes you want to keep from Discovered to Allowed.
- Add client-JS-generated and dynamically computed sizes to
allowedSizesby hand — the scan never sees them. - Switch to
strictand verify that no images return 400.
The images block lives in #/site.json (in the folder's # system folder), shared per folder, keys read case-insensitively:
{
"images": {
"defaultFormat": "webp",
"sizePolicy": "auto",
"onDisallowed": "nearest",
"maxDimension": 2000,
"allowedSizes": ["1200x800x0", "600x400x0", "128x128x1"],
"discoveredSizes": ["1024x1024x0", "300x300x0"]
}
}| images.* | Values | Meaning |
|---|---|---|
defaultFormat | auto | jpeg | png | webp | avif | Default output format. The file extension in linkImage always overrides. |
sizePolicy | open (default) | auto | strict | See the mode table above. |
onDisallowed | nearest | reject | Overrides the default for an unknown size (auto→nearest, strict→reject). |
maxDimension | px, default 2000 | Hard cap on W and H in all modes, including open. 0/unset ⇒ 2000. |
allowedSizes | ["WxHxM", …] | Owner-maintained. Never touched by scan or passive discovery. No cap. |
discoveredSizes | ["WxHxM", …] | Machine-maintained. Append-only + dedupe. Passive growth capped at 100. |
Watch out: strict with an empty allowedSizes rejects every scaled image with 400 (warning strict_no_sizes). Promote at least one real size before you switch.
The full M-mode table (-1…5), the snapping algorithm and every linkImage overload live in the reference pages — see linkImage, scaleImage and the site.json reference.